﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Microsoft.WindowsAzure.Diagnostics;
using Microsoft.WindowsAzure.Diagnostics.Management;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;

namespace BareWeb.FluentAzureDiagnostics
{
    public interface IDiagnosticsCapture
    {
        DiagnosticMonitorConfiguration Configuration { get; }
        DiagnosticsCapture Default();
        DiagnosticsCapture Current();
        void Commit();
    }
    public interface IDiagnosticsCaptureLegacy
    {
        DiagnosticMonitorConfiguration Configuration { get; }
        DiagnosticsCapture Default();
        DiagnosticsCapture Current();
        void Commit();
        DiagnosticsCapture WithoutLogs();
        DiagnosticsCapture WithoutPerformanceCounters();
        DiagnosticsCapture WithoutWindowsEventLogs();
        DiagnosticsCapture WithoutDiagnosticInfrastructureLogs();
        DiagnosticsCapture WithoutDirectories();
        DiagnosticsCapture ForDirectories(DirectoriesBufferConfiguration directories);
        DiagnosticsCapture CheckForConfigurationChangesEvery(TimeSpan timeSpan);
        DiagnosticsCapture  WithOverallQuotaInMBOf(int quotaInMB);
        DiagnosticsCapture WithDirectory(DirectoryConfiguration item);
        DiagnosticsCapture WithDirectory(string path, string container, int directoryQuotaInMB);
        DiagnosticsCapture WithDirectoriesBufferQuotaInMBOf(int directoriesQuotaInMB);
        DiagnosticsCapture TransferDirectoriesEvery(TimeSpan directoriesTransfer);
        DiagnosticsCapture WithInfrastructureLogs(BasicLogsBufferConfiguration infrastructureLogs);
        DiagnosticsCapture WhereInfrastructureLogsAreAbove(LogLevel logLevel);
        DiagnosticsCapture TransferInfrastructureLogsEvery(TimeSpan diagnosticsInfrastructureTransfer);
        DiagnosticsCapture WithInfrastructureLogsQuotaInMBOf(int quotaInMB);

        /// <summary>
        /// Sets default Log settings of "Error", 256MB and 20 Minutes transfer
        /// </summary>
        /// <returns></returns>
        IDiagnosticsLogsFilterable WithLogs();

        IDiagnosticsLogsFilterable WithLogs(BasicLogsBufferConfiguration Logs);
        IDiagnosticsLogsTransferrable WhereLogsAreAbove(LogLevel logLevel);
        DiagnosticsCapture TransferLogsEvery(TimeSpan diagnosticsInfrastructureTransfer);
        DiagnosticsCapture WithLogsQuotaInMBOf(int quotaInMB);
        DiagnosticsCapture WithPerformanceCounter(PerformanceCounterConfiguration item);
        DiagnosticsCapture WithPerformanceCounter(string counterSpecifier, TimeSpan sampleRate);
        DiagnosticsCapture WithPerformanceCountersBufferQuotaInMBOf(int performanceCountersQuotaInMB);
        DiagnosticsCapture TransferPerformanceCountersEvery(TimeSpan performanceCountersTransfer);
        DiagnosticsCapture WithWindowsEventLog(WindowsEventLogsBufferConfiguration windowsEventLog);
        DiagnosticsCapture WithWindowsEventLog(string windowsEventLog);
        DiagnosticsCapture WhereWindowsEventLogAreAbove(LogLevel logLevel);
        DiagnosticsCapture TransferWindowsEventLogEvery(TimeSpan diagnosticsWindowsEventLogTransfer);
        DiagnosticsCapture WithWindowsEventLogQuotaInMBOf(int quotaInMB);
    }

    public interface IDiagnosticsLogsCapture
    {
        /// <summary>
        /// Sets default Log settings of "Error", 256MB and 20 Minutes transfer
        /// </summary>
        /// <returns></returns>
        IDiagnosticsLogsFilterable WithLogs();
        IDiagnosticsLogsFilterable WithLogs(BasicLogsBufferConfiguration Logs);   
    }

    public interface IDiagnosticsLogsFilterable
    {
        IDiagnosticsLogsTransferrable WhereLogsAreAbove(LogLevel logLevel);   
    }

    public interface IDiagnosticsLogsTransferrable
    {
        IDiagnosticsCaptureLegacy TransferLogsEvery(TimeSpan diagnosticsInfrastructureTransfer);
    }

    public class DiagnosticsCapture : IDiagnosticsCaptureLegacy, IDiagnosticsLogsCapture
    {
        private IAzureBlobStorage _fluentBlobStorage;
        private readonly CloudStorageAccount _storageAccount;
        private readonly DiagnosticMonitorConfiguration _configuration;

        internal DiagnosticsCapture(): this(CloudStorageAccount.DevelopmentStorageAccount)
        {
            
        }
        public DiagnosticsCapture(CloudStorageAccount storageAccount)
        {
            _storageAccount = storageAccount;
        }

        public DiagnosticsCapture(CloudStorageAccount storageAccount, IAzureBlobStorage fluentBlobStorage)
        {
            _fluentBlobStorage = fluentBlobStorage;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DiagnosticsCapture"/> class. When used, the string parameters control the creation
        /// of a marker blob in Azure Storage
        /// </summary>
        /// <param name="storageAccount">The storage account.</param>
        /// <param name="deploymentId">The deployment id.</param>
        /// <param name="roleName">Name of the role.</param>
        /// <param name="filename">The filename.</param>
        /// <remarks></remarks>
        public DiagnosticsCapture(CloudStorageAccount storageAccount, string deploymentId, string roleName, string filename) 
            : this(storageAccount, new AzureBlobStorage(storageAccount, deploymentId, roleName, filename))
        {

        }

        public DiagnosticsCapture(CloudStorageAccount storageAccount, DiagnosticMonitorConfiguration configuration) : this(storageAccount)
        {
            _configuration = configuration;
        }

        public DiagnosticMonitorConfiguration Configuration
        {
            get { return _configuration; }
        }

        public DiagnosticsCapture Default()
        {
            return new DiagnosticsCapture(_storageAccount, DiagnosticMonitor.GetDefaultInitialConfiguration()) { _fluentBlobStorage = _fluentBlobStorage }; ;
        }

        public DiagnosticsCapture Current()
        {
            if (!RoleEnvironment.IsAvailable)
            {
                Trace.TraceWarning("Role Environment was not enabled, assuming use of Default Initial Configuration");

                return Default();
            }

            RoleInstanceDiagnosticManager roleInstanceDiagnosticManager =
                _storageAccount.CreateRoleInstanceDiagnosticManager(RoleEnvironment.DeploymentId,
                                                                    RoleEnvironment.CurrentRoleInstance.Role.Name,
                                                                    RoleEnvironment.CurrentRoleInstance.Id);
            DiagnosticMonitorConfiguration config = roleInstanceDiagnosticManager.GetCurrentConfiguration();

            return new DiagnosticsCapture(_storageAccount, config) { _fluentBlobStorage = _fluentBlobStorage };
        }

        public void Commit()
        {
            if (null == _fluentBlobStorage)
            {
                _fluentBlobStorage = new AzureBlobStorage(_storageAccount, RoleEnvironment.DeploymentId, RoleEnvironment.CurrentRoleInstance.Role.Name, "fluentMarker.xml");
            }

            if (_fluentBlobStorage.ShouldCommit())
            {
                if (RoleEnvironment.IsAvailable)
                {
                    RoleInstanceDiagnosticManager roleInstanceDiagnosticManager =
                        _storageAccount.CreateRoleInstanceDiagnosticManager(RoleEnvironment.DeploymentId,
                                                                            RoleEnvironment.CurrentRoleInstance.Role.
                                                                                Name,
                                                                            RoleEnvironment.CurrentRoleInstance.Id);
                    roleInstanceDiagnosticManager.SetCurrentConfiguration(_configuration);
                }
                else
                {
                    Trace.TraceWarning("Windows Azure Fluent Diagnostics did not SetCurrentConfiguration since the RoleEnvironment was not available.");
                }

                _fluentBlobStorage.PutFluentMarker();
            }
            else
            {
                Trace.TraceInformation("Window Azure Fluent Diagnostics did not update the configuration as a marker file exists. This marker file in the wad-control-container container prevents duplicate updates per roleStart");
            }
        }


        #region Withouts!
        public DiagnosticsCapture WithoutLogs()
        {
            _configuration.Logs = null;
            return this;
        }
        public DiagnosticsCapture WithoutPerformanceCounters()
        {
            _configuration.PerformanceCounters = null;
            return this;
        }
        public DiagnosticsCapture WithoutWindowsEventLogs()
        {
            _configuration.WindowsEventLog = null;
            return this;
        }
        public DiagnosticsCapture WithoutDiagnosticInfrastructureLogs()
        {
            _configuration.DiagnosticInfrastructureLogs = null;
            return this;
        }
        public DiagnosticsCapture WithoutDirectories()
        {
            _configuration.Directories = null;
            return this;
        }

        #endregion

        public DiagnosticsCapture ForDirectories(DirectoriesBufferConfiguration directories)
        {
            _configuration.Directories = directories;
            
            return this;
        }

        public DiagnosticsCapture CheckForConfigurationChangesEvery(TimeSpan timeSpan)
        {
            _configuration.ConfigurationChangePollInterval = timeSpan;
            return this;
        }

        public DiagnosticsCapture  WithOverallQuotaInMBOf(int quotaInMB)
        {
            _configuration.OverallQuotaInMB = quotaInMB;
            return this;
        }

        public DiagnosticsCapture WithDirectory(DirectoryConfiguration item)
        {
            EnsureDirectories();

            _configuration.Directories.DataSources.Add(item);
            return this;
        }

        private void EnsureDirectories()
        {
            if (_configuration.Directories == null)
            {
                _configuration.Directories = new DirectoriesBufferConfiguration();
            }
        }

        public DiagnosticsCapture WithDirectory(string path, string container, int directoryQuotaInMB)
        {
            var directoryConfiguration = new DirectoryConfiguration
                                             {
                                                 Container = container,
                                                 DirectoryQuotaInMB = directoryQuotaInMB,
                                                 Path = path
                                             };

            return WithDirectory(directoryConfiguration);
        }

        public DiagnosticsCapture WithDirectoriesBufferQuotaInMBOf(int directoriesQuotaInMB)
        {
            EnsureDirectories();

            _configuration.Directories.BufferQuotaInMB = directoriesQuotaInMB;
            return this;
        }

        public DiagnosticsCapture TransferDirectoriesEvery(TimeSpan directoriesTransfer)
        {
            EnsureDirectories();

            _configuration.Directories.ScheduledTransferPeriod = directoriesTransfer;
            return this;
        }

        public DiagnosticsCapture WithInfrastructureLogs(BasicLogsBufferConfiguration infrastructureLogs)
        {
            _configuration.DiagnosticInfrastructureLogs = infrastructureLogs;
            return this;
        }

        public DiagnosticsCapture WhereInfrastructureLogsAreAbove(LogLevel logLevel)
        {
            EnsureInfrastructureLogs();

            _configuration.DiagnosticInfrastructureLogs.ScheduledTransferLogLevelFilter = logLevel;
            return this;
        }

        private void EnsureInfrastructureLogs()
        {
            if (_configuration.DiagnosticInfrastructureLogs == null)
            {
                _configuration.DiagnosticInfrastructureLogs = new BasicLogsBufferConfiguration();
            }
        }

        public DiagnosticsCapture TransferInfrastructureLogsEvery(TimeSpan diagnosticsInfrastructureTransfer)
        {
            EnsureInfrastructureLogs();

            _configuration.DiagnosticInfrastructureLogs.ScheduledTransferPeriod = diagnosticsInfrastructureTransfer;
            return this;
        }

        public DiagnosticsCapture WithInfrastructureLogsQuotaInMBOf(int quotaInMB)
        {
            EnsureInfrastructureLogs();
            _configuration.DiagnosticInfrastructureLogs.BufferQuotaInMB = quotaInMB;
            return this;
        }

        /// <summary>
        /// Sets default Log settings of "Error", 256MB and 20 Minutes transfer
        /// </summary>
        /// <returns></returns>
        public IDiagnosticsLogsFilterable WithLogs()
        {
            EnsureLogs();

            _configuration.Logs.ScheduledTransferLogLevelFilter = LogLevel.Error;
            _configuration.Logs.BufferQuotaInMB = 256;
            _configuration.Logs.ScheduledTransferPeriod = TimeSpan.FromMinutes(20D);

            return this as IDiagnosticsLogsFilterable;
        }

        public IDiagnosticsLogsFilterable WithLogs(BasicLogsBufferConfiguration Logs)
        {
            _configuration.Logs = Logs;
            return this as IDiagnosticsLogsFilterable;
        }

        public DiagnosticsCapture WhereLogsAreAbove(LogLevel logLevel)
        {
            EnsureLogs();

            _configuration.Logs.ScheduledTransferLogLevelFilter = logLevel;
            return this;
        }

        private void EnsureLogs()
        {
            if (_configuration.Logs == null)
            {
                _configuration.Logs = new BasicLogsBufferConfiguration();
            }
        }

        public DiagnosticsCapture TransferLogsEvery(TimeSpan diagnosticsInfrastructureTransfer)
        {
            EnsureLogs();

            _configuration.Logs.ScheduledTransferPeriod = diagnosticsInfrastructureTransfer;
            return this;
        }

        public DiagnosticsCapture WithLogsQuotaInMBOf(int quotaInMB)
        {
            EnsureLogs();
            _configuration.Logs.BufferQuotaInMB = quotaInMB;
            return this;
        }

        public DiagnosticsCapture WithPerformanceCounter(PerformanceCounterConfiguration item)
        {
            EnsurePerformanceCounters();

            _configuration.PerformanceCounters.DataSources.Add(item);
            return this;
        }

        private void EnsurePerformanceCounters()
        {
            if (_configuration.PerformanceCounters == null)
            {
                _configuration.PerformanceCounters = new PerformanceCountersBufferConfiguration();
            }
        }

        public DiagnosticsCapture WithPerformanceCounter(string counterSpecifier, TimeSpan sampleRate)
        {
            var performanceCounterConfiguration = new PerformanceCounterConfiguration
            {
                CounterSpecifier = counterSpecifier,
                SampleRate = sampleRate
            };

            return WithPerformanceCounter(performanceCounterConfiguration);
        }

        public DiagnosticsCapture WithPerformanceCountersBufferQuotaInMBOf(int performanceCountersQuotaInMB)
        {
            EnsurePerformanceCounters();

            _configuration.PerformanceCounters.BufferQuotaInMB = performanceCountersQuotaInMB;
            return this;
        }

        public DiagnosticsCapture TransferPerformanceCountersEvery(TimeSpan performanceCountersTransfer)
        {
            EnsurePerformanceCounters();

            _configuration.PerformanceCounters.ScheduledTransferPeriod = performanceCountersTransfer;
            return this;
        }

        public DiagnosticsCapture WithWindowsEventLog(WindowsEventLogsBufferConfiguration windowsEventLog)
        {
            _configuration.WindowsEventLog = windowsEventLog;
            return this;
        }

        public DiagnosticsCapture WithWindowsEventLog(string windowsEventLog)
        {
            EnsureWindowsEventLog();

            _configuration.WindowsEventLog.DataSources.Add(windowsEventLog);
            return this;
        }

        public DiagnosticsCapture WhereWindowsEventLogAreAbove(LogLevel logLevel)
        {
            EnsureWindowsEventLog();

            _configuration.WindowsEventLog.ScheduledTransferLogLevelFilter = logLevel;
            return this;
        }

        private void EnsureWindowsEventLog()
        {
            if (_configuration.WindowsEventLog == null)
            {
                _configuration.WindowsEventLog = new WindowsEventLogsBufferConfiguration();
            }
        }

        public DiagnosticsCapture TransferWindowsEventLogEvery(TimeSpan diagnosticsWindowsEventLogTransfer)
        {
            EnsureWindowsEventLog();

            _configuration.WindowsEventLog.ScheduledTransferPeriod = diagnosticsWindowsEventLogTransfer;
            return this;
        }

        public DiagnosticsCapture WithWindowsEventLogQuotaInMBOf(int quotaInMB)
        {
            EnsureWindowsEventLog();
            _configuration.WindowsEventLog.BufferQuotaInMB = quotaInMB;
            return this;
        }
    }
}
